/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.core; import java.awt.*; import java.beans.*; import java.io.*; import java.lang.ref.WeakReference; import java.net.URL; import java.util.*; import java.util.List; // override java.awt.List import java.util.Map; // override javax.help.Map import javax.swing.*; import javax.swing.plaf.ComponentUI; import org.openide.TopManager; import org.openide.awt.HtmlBrowser; import org.openide.text.EditorSupport; import org.openide.util.*; import org.openide.util.io.SafeException; import org.openide.windows.*; import javax.help.*; import javax.help.event.*; import javax.help.plaf.HelpContentViewerUI; import javax.help.plaf.basic.BasicHelpUI; /** Help implementation using the JavaHelp 1.0 system. * Should only be loaded if JavaHelp is installed in the class path. * @author Jesse Glick */ class JavaHelp implements Help.Impl { /** The default instance of the JavaHelp implementation; there *should not be more than one. */ private static JavaHelp DEFAULT =null; /** Make a JavaHelp implementation of the Help.Impl interface. *Or, use {@link #getDefaultJavaHelp}. */ public JavaHelp() { DEFAULT = this; } /** Get the default instance of the JavaHelp implementation. * @return the default instance */ static JavaHelp getDefaultJavaHelp() { if (DEFAULT == null) return new JavaHelp (); else return DEFAULT; } /** Base resource string to the dummy master help set. *Just contains a title and empty standard content navigators *permitting other help sets to be merged into it. */ private static final String MASTER_RESOURCE ="org.netbeans.core.resources.masterHelpSet"; // NOI18N /** As MASTER_RESOURCE, but now the file extension. */ private static final String MASTER_RESOURCE_EXT = "xml"; // NOI18N /** A helpID present only in the master help set; *however, when displayed by {@link #showHelp} as the helpID in a context, *the master help set (with merged-in children) will be shown instead, *with no change made to the content pane. *Also, this is the help ID mapped to the "default" page in the master help viewer. */ private static final String MASTER_ID ="org.netbeans.core.JavaHelp.MASTER_ID"; // NOI18N /** Internal name of the window manager {@link Mode} to be used *by default for {@link HelpComponent}s. */ private static final String JAVAHELP_MODE ="JavaHelp"; // window manager mode // NOI18N /** Map from help set URLs to the help sets themselves. *Type <CODE>Map<URL,HelpSet&rt;</CODE>. */ private Map setsByUrl = new HashMap (); // Map<URL,HelpSet> /** Map from module code name to the home ID for that module. *Of type <CODE>Map<String,String></CODE>. */ private Map homesByCode = new HashMap (); // Map<String,String> /** Map from display names of modules (or the displayed name under Features, *often help set title) to the home IDs. Note that the display names *are guaranteed to be unique, using suffixes if needed. *Of type <CODE>Map<String,String></CODE>. */ private Map homesByDisplay =new HashMap (); // Map<String,String> /** Map from help set URLs to module code names. *Of type <CODE>Map<URL,String></CODE>. * @associates String */ private Map codesByUrl =new HashMap (); // Map<URL,String> /** Map from help set URLs to module display names (or help set titles). *Of type <CODE>Map<URL,String></CODE>. * @associates String */ private Map displaysByUrl =new HashMap (); // Map<URL,String> /** Set of home IDs which should be prominently displayed. * Of type <code>Set<String&rt;</code>. */ private Set distinguishedHomeIDs = new HashSet (); // Set<String> /** The master help set. */ private HelpSet master =null; /** Get the master help set that others will be merged into. * @return the master help set */ private HelpSet getMaster() { if (master == null) { ClassLoader loader = JavaHelp.class.getClassLoader (); try { master = new HelpSet (loader, NbBundle.getLocalizedFile (MASTER_RESOURCE, MASTER_RESOURCE_EXT, Locale.getDefault (), loader)); } catch (HelpSetException hse) { TopManager.getDefault ().notifyException (hse); } } return master; } /** Add a help set to the system from a module. * @param resource The URL to the help set (<CODE>.hs</CODE>) file. * @param moduleCodeName The module code name (mostly used to look up context *help from {@link ModuleNode.Item}). * @param moduleDisplayName The module's display name, used as a backup display *name for the help in case the <CODE><TITLE&rt;</CODE> *tag is missing. */ public void addHelpSet(URL resource,String moduleCodeName,String moduleDisplayName) { try { HelpSet sub = new HelpSet (TopManager.getDefault ().currentClassLoader (), resource); String displayName = sub.getTitle (); boolean distinguished = displayName != null && displayName.endsWith ("*"); // NOI18N if (distinguished) { displayName = displayName.substring (0, displayName.length () - 1); sub.setTitle (displayName); //System.err.println("Found distinguished help set: " + displayName); } if (displayName == null || displayName.equals ("")) displayName = moduleDisplayName; // NOI18N if (homesByDisplay.containsKey (displayName)) { int i = 2; String test; while (homesByDisplay.containsKey (test = Main.getString ("LBL_HelpSet_Duplicate", displayName, String.valueOf (i)))) i++; displayName = test; } javax.help.Map.ID homeID = sub.getHomeID (); if (homeID != null) { homesByCode.put (moduleCodeName, homeID.id); homesByDisplay.put (displayName, homeID.id); if (distinguished) distinguishedHomeIDs.add (homeID.id); } setsByUrl.put (resource, sub); codesByUrl.put (resource, moduleCodeName); displaysByUrl.put (resource, displayName); getMaster ().add (sub); } catch (HelpSetException hse) { TopManager.getDefault ().notifyException (hse); } } /** Remove a help set from the system (i.e. when the module is uninstalled). * @param resource The URL to the help set to be removed. */ public void removeHelpSet(URL resource) { String code = (String) codesByUrl.remove (resource); if (code != null) homesByCode.remove (code); String display = (String) displaysByUrl.remove (resource); if (display != null) { String home = (String) homesByDisplay.remove (display); if (home != null) distinguishedHomeIDs.remove (home); } HelpSet hs = (HelpSet) setsByUrl.remove (resource); if (hs != null) getMaster ().remove (hs); } /** Get the master index name. * @return a display name */ public String getMasterDisplayName () { return Main.getString ("LBL_MasterIndex"); } /** Get the master ID. * Will instead return nothing if either: * <ol> * <li>There are no help sets installed, in which case the whole * thing would not even be useful to display. * <li>There is exactly one help set installed, and it specifies * a home ID, meaning that all possible help information is already * displayed in its own frame. * </ol> * @return the dummy help ID, or <code>null</code> */ public String getMasterID () { if (setsByUrl.size () == 0 || (setsByUrl.size () == 1 && homesByCode.size () == 1)) return null; else return MASTER_ID; } /** Test whether a helpset should be displayed prominently. * Currently this is only true if the title of the help set * as originally found in XML ended with an asterisk, as a * special hack marker. * @param homeID the home ID to test * @return whether the helpset should be prominent */ public boolean isDistinguished (String homeID) { return distinguishedHomeIDs.contains (homeID); } /** Show some help. *This is the basic call which should be used externally *and is the result of {@link TopManager#showHelp}. *Handles null contexts, missing or null help IDs, and null URLs. *If possible, looks for an existing help window in the proper mode *on this workspace that is showing the same help set as this context *help object requests. If there is any problem, shows the master set *instead, or it may also create a new help window. *All display is done using {@link HelpComponent}. *Works correctly if invoked while a dialog is open--creates a new modal *dialog with the help. * @param ctx the help context to display */ public void showHelp(HelpCtx ctx) { //System.err.println("showing help: " + ctx); if (ctx == null) ctx = HelpCtx.DEFAULT_HELP; NbPresenter dlg = NbPresenter.currentModalDialog; if (dlg == null) { //System.err.println("showing as non-dialog"); String helpID = ctx.getHelpID (); HelpSet hs = findHelpSetForID (helpID); //System.err.println("desired helpset: " + hs.getTitle ()); HelpComponent comp = findExistingViewer (hs); //System.err.println("existing viewer: " + comp); if (comp == null) { //System.err.println("creating new viewer"); comp = new HelpComponent (getJHelp (ctx)); Workspace ws = TopManager.getDefault ().getWindowManager ().getCurrentWorkspace (); Mode m = ws.findMode (JAVAHELP_MODE); if (m == null) m = ws.createMode (JAVAHELP_MODE, Main.getString ("LBL_JavaHelp_mode"), JavaHelp.class.getClassLoader ().getResource ("org/netbeans/core/resources/actions/moduleHelp.gif")); // NOI18N // Reuse editor mode bounds, possibly scaled to 3/4, but assuming 920x660 desirable size: Mode edmode = ws.findMode (EditorSupport.EDITOR_MODE); if (edmode != null) { Rectangle r = edmode.getBounds (); int width = r.width * 3 / 4; if (width < 920) width = r.width; int height = r.height * 3 / 4; if (height < 660) height = r.height; m.setBounds (new Rectangle (r.x, r.y, width, height)); } if (m.canDock (comp)) m.dockInto (comp); } if (hs.equals (getMaster ())) { //System.err.println("desired was master help set"); if (helpID != null && ! helpID.equals (MASTER_ID)) warnBadID (helpID); } else { //System.err.println("regular help display"); try { comp.getJHelp ().setCurrentID (helpID); } catch (BadIDException bide) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N bide.printStackTrace (); } } //System.err.println("opening " + comp + "..."); comp.open (); comp.requestFocus (); } else { //System.err.println("showing as dialog"); JDialog d1 = new JDialog (dlg); d1.getContentPane ().add ("Center", getJHelp (ctx)); // NOI18N d1.setModal (true); d1.setTitle (Main.getString ("CTL_Help")); d1.pack (); d1.show (); } } /** Find the proper help set for an ID. * @param id the ID to look up * @return the proper help set (master if not otherwise found) */ private HelpSet findHelpSetForID (String id) { if (id == null) return getMaster (); Iterator it = setsByUrl.values ().iterator (); while (it.hasNext ()) { HelpSet hs = (HelpSet) it.next (); if (hs.getCombinedMap ().isValidID (id, hs)) return hs; } return getMaster (); } /** Find the best viewer to show this help set in. * Looks for one that is in the current workspace; has the same current help set * (and not the master help set, unless the master was requested); is visible; * and is in the JavaHelp mode. Criteria in that order. * <p>Note: since deserialization now discards previously hidden viewers, this * method is no longer so important: especially searching hidden viewers is * not critical. To force it to be visible in order to return it, use: * spec=4 vis=8 ws=2 mod=1 min=11 (bestScore = 10) * @param hs the desired help set * @return an existing viewer for that help set, or for the master set, or <code>null</code> */ private HelpComponent findExistingViewer (HelpSet hs) { boolean isMaster = hs.equals (getMaster ()); // Current best match HelpComponent best = null; // Current best score; minimum cut-off before we create a new one int bestScore = 6; //System.err.println("bestScore=" + bestScore); Set visible = TopComponent.getRegistry ().getOpened (); WindowManager wm = TopManager.getDefault ().getWindowManager (); Workspace curr = wm.getCurrentWorkspace (); Workspace[] wss = wm.getWorkspaces (); for (int i = 0; i < wss.length; i++) { Workspace ws = wss[i]; //System.err.println("ws=" + ws.getName ()); boolean thisIsCurrWs = curr.equals (ws); Iterator modes = ws.getModes ().iterator (); while (modes.hasNext ()) { Mode mode = (Mode) modes.next (); if (mode == null) continue; //System.err.println("mode=" + mode.getName()); // Sometimes we get a null mode, or maybe a mode with a null name...? boolean thisIsCurrMode = JAVAHELP_MODE.equals (mode.getName ()); TopComponent[] tcs = mode.getTopComponents (); for (int j = 0; j < tcs.length; j++) { if (! (tcs[j] instanceof HelpComponent)) continue; HelpComponent hc = (HelpComponent) tcs[j]; JHelp jh = hc.getJHelp (); if (jh == null) { //System.err.println("uninitialized HelpComponent found!"); continue; } HelpSet tesths = jh.getModel ().getHelpSet (); //System.err.println("tesths=" + tesths.getTitle ()); boolean testIsMaster = tesths.equals (getMaster ()); if (! hs.equals (tesths) && ! testIsMaster) { //System.err.println("skipping inappropriate helpset"); continue; } int score = 0; // Help component is visible: if (visible.contains (hc)) score += 4; // Specific help: if (isMaster || ! testIsMaster) score += 8; // On the same workspace: if (thisIsCurrWs) score += 2; // On the same mode: if (thisIsCurrMode) score += 1; //System.err.println("score=" + score); if (score > bestScore) { //System.err.println("(new best)"); bestScore = score; best = hc; } } } } return best; } /** Get home IDs by code name. * @see #homesByCode * @return a map */ public Map getHomesByCode() { return homesByCode; } /** Get the home IDs by display name. * @see #homesByDisplay * @return a map */ public Map getHomesByDisplay() { return homesByDisplay; } /** Create a new JHelp component displaying the given help. * @param ctx the help to display (may be incorrect or partial) * @return a new JHelp component, hopefully displaying the desired help */ private JHelp getJHelp(HelpCtx ctx) { return getJHelp (ctx.getHelpID (), ctx.getHelp ()); } /** Warn that an ID was not found in any help set. * @param id the help ID */ private static void warnBadID (String id) { // PLEASE DO NOT COMMENT OUT...localized warning System.err.println (Main.getString ("MSG_jh_id_not_found", id)); } /** Create a new JHelp component displaying some help. *Handles {@link #MASTER_ID}, as well as help IDs *that were not found in any help set, various exceptions, etc. *Should always return a valid JHelp, and not throw any exceptions *based on the arguments. * @param helpID a help ID string to display, may be <CODE>null</CODE> * @param url a URL to display, may be <CODE>null</CODE>; lower priority than the help ID * @return a valid JHelp, hopefully displaying the requested help */ private synchronized JHelp getJHelp(String helpID,URL url) { //System.err.println("getting a helpAndSet: " + helpID + " " + url); try { if (MASTER_ID.equals (helpID)) { // Special "helpID" only used to display master help. // NOI18N //System.err.println("uncached master"); return createMasterJHelp (); } else { // Regular help context. HelpSet hs = getMaster (); javax.help.Map.ID id = null; if (helpID != null) { Iterator it = setsByUrl.values ().iterator (); while (it.hasNext ()) { HelpSet test = (HelpSet) it.next (); if (test.getCombinedMap ().isValidID (helpID, test)) { hs = test; id = javax.help.Map.ID.create (helpID, test); //System.err.println("found in helpset cache: " + hs.getTitle () + " " + id); break; } } if (hs == getMaster ()) { warnBadID (helpID); } } JHelp jHelp = new JHelp (hs); if (id != null) jHelp.setCurrentID (id); else if (url != null) jHelp.setCurrentURL (url); //System.err.println("uncached: " + jHelp + " " + hs.getTitle ()); return jHelp; } } catch (BadIDException bide) { // Should not happen: TopManager.getDefault ().notifyException (bide); return createMasterJHelp (); } catch (InvalidHelpSetContextException ihsce) { // Should not happen: TopManager.getDefault ().notifyException (ihsce); return createMasterJHelp (); } } /** Create & return a JHelp with the master set. * @return the new JHelp */ private JHelp createMasterJHelp () { JHelp jh = new JHelp (getMaster ()); try { jh.setCurrentID (MASTER_ID); } catch (BadIDException bide) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N bide.printStackTrace (); } return jh; } /** A top component displaying a single JHelp. * Support cloning and window system serialization. */ public static class HelpComponent extends CloneableTopComponent { /** The JHelp component being displayed (usually non-null). */ private JHelp jHelp =null; /** Serial version. */ private static final long serialVersionUID =882544693090803112L; /** Whether to discard rather than deserialize. */ private transient boolean discard = false; /** Public constructor, only for use during deserialization. */ public HelpComponent() { //System.err.println("creating HelpComponent"); SwingUtilities.invokeLater (new Runnable () { public void run () { //System.err.println("creating HelpComponent (II)"); // Should not normally be visible: setName (Main.getString ("LBL_Empty_HelpComponent")); } }); } /** Regular constructor. * @param jHelp the JHelp component to display */ public HelpComponent(JHelp jHelp) { this (); setJHelp (jHelp); } /** Get the help context. * @return context help for this component */ public HelpCtx getHelpCtx() { return new HelpCtx (HelpComponent.class); } /** Get the associated JHelp component. * @return the JHelp component */ public JHelp getJHelp() { return jHelp; } /** Install a JHelp component during deserialization. *Should not be called twice, or after the regular constructor. * @param nue the JHelp to install */ public synchronized void setJHelp(JHelp nue) { //System.err.println("installing jHelp"); if (jHelp != null) throw new IllegalArgumentException ("Cannot reinstall the JHelp component"); // NOI18N if (nue == null) throw new NullPointerException ("Not supported to un-set JHelp components"); // NOI18N jHelp = nue; SwingUtilities.invokeLater (new Runnable () { public void run () { //System.err.println("setJHelp (II)"); removeAll (); setLayout (new BorderLayout ()); add (jHelp, BorderLayout.CENTER); setName (jHelp.getModel ().getHelpSet ().getTitle ()); revalidate (); } }); } /** Read component from disk. * @serialData Super method; then Boolean (not boolean!) discard; String id; then URL url. * Either id or url may be <code>null</code>. * @param in the input stream to read from * @throws IOException for the usual reasons * @throws ClassNotFoundException for the usual reasons */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal (in); //System.err.println("reading HelpComponent"); // Backwards compatiblity: accepts missing "discard". // NOI18N String id; Object readMe = in.readObject (); if (readMe instanceof String) { discard = false; id = (String) readMe; } else { discard = ((Boolean) readMe).booleanValue (); id = (String) in.readObject (); } URL url = (URL) in.readObject (); if (! discard) { try { setJHelp (getDefaultJavaHelp ().getJHelp (id, url)); } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N e.printStackTrace (); throw new SafeException (e); } } } /** Writes to serialization. * @serialData Super, then Boolean discard, String id, URL url. * Either id or url may be <code>null</code>. * @param out stream to write to * @throws IOException for the usual reasons */ public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal (out); //System.err.println("writing: " + jHelp); // If not open anywhere, please discard me: out.writeObject (new Boolean (! isOpened ())); Object toWrite1 = null; Object toWrite2 = null; try { if (jHelp != null) { javax.help.Map.ID id = jHelp.getModel ().getCurrentID (); if (id != null) toWrite1 = id.id; toWrite2 = jHelp.getModel ().getCurrentURL (); } } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N e.printStackTrace (); throw new SafeException (e); } finally { out.writeObject (toWrite1); out.writeObject (toWrite2); } } /** Possibly discard this component rather than deserialize. * @return this, or <code>null</code> if it was hidden when saved and should not be deserialized * @throws ObjectStreamException will not be thrown */ public Object readResolve () throws ObjectStreamException { //System.err.println("HelpComponent.readResolve; discard=" + discard); return discard ? null : this; } /** Create a cloned viewer. * Tries to open in the correct mode. * @return a viewer displaying the same help ID and/or URL */ protected CloneableTopComponent createClonedObject() { //System.err.println("cloning component..."); CloneableTopComponent toRet; if (jHelp == null) { toRet = new HelpComponent (); } else { javax.help.Map.ID id = jHelp.getModel ().getCurrentID (); toRet = new HelpComponent (getDefaultJavaHelp ().getJHelp (id == null ? null : id.id, jHelp.getModel ().getCurrentURL ())); } Mode m = TopManager.getDefault ().getWindowManager ().getCurrentWorkspace ().findMode (JAVAHELP_MODE); if (m != null && m.canDock (toRet)) m.dockInto (toRet); return toRet; } /** Avoid problems during L&F changes. * JavaHelp components seem to blank out if updateUI() is called * on them, so it is necessary to make sure they are not part * of the container while this method is being called. It also * works to just throw an exception and stop the process, but * that is rather rude! */ public void updateUI () { //System.err.println("HelpComponent.updateUI"); if (jHelp != null) { //System.err.println("removing jHelp..."); remove (jHelp); //System.err.println("super.updateUI..."); super.updateUI (); //System.err.println("will call rest later..."); SwingUtilities.invokeLater (new Runnable () { public void run () { //System.err.println("re-adding fresh jHelp..."); // Reusing the same model does not seem to work, the last-selected // ID will not be correctly reselected. JHelp jh2 = new JHelp (jHelp.getModel ().getHelpSet ()); add (jh2, BorderLayout.CENTER); jHelp = jh2; //System.err.println("revalidating..."); revalidate (); } }); } } } /** The current UIDefaults object, whose property changes will be listened to. */ private static UIDefaults curr = UIManager.getLookAndFeelDefaults (); /** A listener to changes in the UI which attempts to keep the custom UIs *installed over the JavaHelp defaults. */ private static PropertyChangeListener uiListener =new PropertyChangeListener () { public void propertyChange (PropertyChangeEvent ev) { //System.err.println("uiListener.pC"); if (! BrowserContentViewerUI.class.getName ().equals (curr.get ("HelpContentViewerUI")) || // NOI18N ! ToolbarlessHelpUI.class.getName ().equals (curr.get ("HelpUI"))) { // NOI18N //System.err.println("uiListener: out of date"); //System.err.println("HelpContentViewerUI=" + curr.get ("HelpContentViewerUI")); //System.err.println("HelpUI=" + curr.get ("HelpUI")); curr.removePropertyChangeListener (uiListener); curr.putDefaults (new Object[] { "HelpContentViewerUI", BrowserContentViewerUI.class.getName (), // NOI18N "HelpUI", ToolbarlessHelpUI.class.getName () // NOI18N }); curr.addPropertyChangeListener (uiListener); } } }; static { curr.addPropertyChangeListener (uiListener); uiListener.propertyChange (null); UIManager.addPropertyChangeListener (new PropertyChangeListener () { public void propertyChange (PropertyChangeEvent ev) { //System.err.println("UIManager PCE: " + ev.getPropertyName ()); if ("lookAndFeel".equals (ev.getPropertyName ())) { curr.removePropertyChangeListener (uiListener); curr = UIManager.getLookAndFeelDefaults (); curr.addPropertyChangeListener (uiListener); uiListener.propertyChange (null); } } }); } /** Hack to take the toolbar out of the JHelp window. It is big and ugly * and the web browser already has a toolbar which contains back and * forward buttons which also work better (e.g. with frames). */ public static class ToolbarlessHelpUI extends BasicHelpUI { /** Create the proper UI. * @param comp the JHelp component to render * @return the desired UI */ public static ComponentUI createUI(JComponent comp) { return new ToolbarlessHelpUI ((JHelp) comp); } static final long serialVersionUID =-4593005151581696837L; /** Create a new UI. * @param jHelp the component to render */ public ToolbarlessHelpUI(JHelp jHelp) { super (jHelp); } /** Installs the UI. *Just tries to remove the JToolBar. * @param c the component to render */ public void installUI(JComponent c) { super.installUI (c); Component[] components = c.getComponents (); for (int i = 0; i < components.length; i++) { if (components[i] instanceof JToolBar) { c.remove (components[i]); return; } } // PLEASE DO NOT COMMENT OUT if (Boolean.getBoolean ("netbeans.debug.exceptions")) // NOI18N System.err.println ("Warning: could not remove toolbar from JHelp UI"); } } /** Alternate UI for the content browser. * Instead of using the slow and buggy Swing Text HTML support, * uses {@link #HtmlBrowser} embedded browser. */ public static class BrowserContentViewerUI extends HelpContentViewerUI implements Serializable, PropertyChangeListener, HelpModelListener { /** Create UI for a component. * @param comp the component to render * @return the UI */ public static ComponentUI createUI(JComponent comp) { return new BrowserContentViewerUI (); } /** The actual browser component. */ private HtmlBrowser browser; /** The content viewer being rendered. */ private JHelpContentViewer viewer; static final long serialVersionUID =2565972402655318909L; /** Create a new UI. */ public BrowserContentViewerUI() { //System.err.println("creating UI..."); } /** Install UI for the component. * @param c the content navigator to render */ public void installUI(JComponent c) { //System.err.println("installing UI to " + c); viewer = (JHelpContentViewer) c; viewer.addPropertyChangeListener (this); TextHelpModel model = viewer.getModel (); // [PENDING] should viewer.addHelpModelListener (this) be used instead?? if (model != null) model.addHelpModelListener (this); // No TextHelpModelListener--we do not support highlighting. browser = new HtmlBrowser (); browser.setEnableHome (false); browser.setEnableLocation (false); browser.addPropertyChangeListener (this); viewer.setLayout (new BorderLayout ()); viewer.add (browser, BorderLayout.CENTER); } /** Uninstall UI from the component. * @param c the content navigator */ public void uninstallUI(JComponent c) { //System.err.println("uninstalling UI from " + c); JHelpContentViewer jhcv = (JHelpContentViewer) c; viewer.removePropertyChangeListener (this); TextHelpModel model = viewer.getModel (); if (model != null) model.removeHelpModelListener (this); if (browser != null) browser.removePropertyChangeListener (this); browser = null; viewer.setLayout (null); viewer.removeAll (); viewer = null; } /** Handle changes in browser.title and viewer.helpModel. * @param ev the event */ public void propertyChange(PropertyChangeEvent ev) { //System.err.println("UI prop chg: [" + ev.getPropertyName () + "] " + ev.getOldValue () + " -> " + ev.getNewValue ()); if (ev.getSource () == browser) { // Really a PROP_TITLE change, but what the heck. if (browser == null) return; URL url = browser.getDocumentURL (); if (url == null) return; // Probably should never happen. TextHelpModel model = viewer.getModel (); if (model != null) { //System.err.println("Browser url set to " + url); try { model.removeHelpModelListener (this); // model.setDocumentTitle (???); javax.help.Map.ID id = model.getHelpSet ().getCombinedMap ().getIDFromURL (url); if (id != null) { //System.err.println("(setting by id)"); try { model.setCurrentID (id); } catch (InvalidHelpSetContextException ihsce) { // Should probably not happen: TopManager.getDefault ().notifyException (ihsce); model.setCurrentURL (url); } } else { //System.err.println("(setting by URL)"); model.setCurrentURL(url); } } finally { model.addHelpModelListener (this); } } } else { // Viewer. if ("helpModel".equals (ev.getPropertyName ())) { //System.err.println("Changing help model..."); HelpModel old = (HelpModel) ev.getOldValue (); if (old != null) old.removeHelpModelListener (this); HelpModel nue = (HelpModel) ev.getNewValue (); if (nue != null) nue.addHelpModelListener (this); } } } /** Handle changes in the help model's ID. * @param ev the event */ public void idChanged(HelpModelEvent ev) { //System.err.println("UI: ID changed to " + ev.getID () + " " + ev.getURL ()); if (browser != null) { try { browser.removePropertyChangeListener (this); browser.setURL (ev.getURL ()); } finally { browser.addPropertyChangeListener (this); } } } } } /* * Log * 26 Gandalf 1.25 1/18/00 Jesse Glick Setting mode bounds to * something reasonable. * 25 Gandalf 1.24 1/13/00 Jaroslav Tulach I18N * 24 Gandalf 1.23 12/30/99 Jaroslav Tulach New dialog for * notification of exceptions. * 23 Gandalf 1.22 12/21/99 Jesse Glick Putting User's Guide off * from the rest of the help menu items to visually distinguish it. * 22 Gandalf 1.21 12/20/99 Jesse Glick Finally got L&F changes * to completely work despite JavaHelp bug. * 21 Gandalf 1.20 12/20/99 Jesse Glick Serialization * improvements. * 20 Gandalf 1.19 12/20/99 Jesse Glick Reorganized Help | * Features to be Help | Documentation, killing old UG browse action, * better labelling of master help set, etc. * 19 Gandalf 1.18 11/26/99 Patrik Knakal * 18 Gandalf 1.17 11/10/99 Jesse Glick Fixing exception during * L&F change with JavaHelp component open. * 17 Gandalf 1.16 11/5/99 Jesse Glick Workaround for winsys * bug. * 16 Gandalf 1.15 11/4/99 Jesse Glick Overhauled JavaHelp: * esp. treatment of existing and new components. Should simplify impl and * fix some bugs and improve UI too. * 15 Gandalf 1.14 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 14 Gandalf 1.13 10/6/99 Jesse Glick Changing UI setting to * occur more deterministically (prop chg listeners etc.). * 13 Gandalf 1.12 10/5/99 Jesse Glick Aggressiveness of UI * overriding seems to be improved. * 12 Gandalf 1.11 10/5/99 Jesse Glick Less ugly prevention of * feedback loops with propchg's and idevent's in browser UI. * 11 Gandalf 1.10 10/5/99 Jesse Glick Forcibly removing the * oversized ugly toolbar from the help window. It does not work as well * as the browser's own buttons anyway. * 10 Gandalf 1.9 10/5/99 Jesse Glick Setting Help | Features * entry names by JavaHelp helpset title, rather than module display name, * whenever possible. * 9 Gandalf 1.8 9/30/99 Jesse Glick * 8 Gandalf 1.7 9/29/99 Jesse Glick Commented-out printlns. * 7 Gandalf 1.6 9/29/99 Jesse Glick Bugfix (infinite loop). * 6 Gandalf 1.5 9/29/99 Jesse Glick Added custom UI based on * installed HtmlBrowser. * 5 Gandalf 1.4 9/29/99 Jesse Glick Now have distinction * between the HelpSet's used for individual module help, and the master * merged help which is available separately. Also using TopComponent's * for the help browser (unless invoked in a dialog). * 4 Gandalf 1.3 9/28/99 Jesse Glick Starting with a nonempty * master help set, so that modules can add navigator views to it * correctly. * 3 Gandalf 1.2 9/28/99 Jesse Glick Just removing CR's. * 2 Gandalf 1.1 9/28/99 Jesse Glick * 1 Gandalf 1.0 9/27/99 Jesse Glick * $ */